#include <ncurses.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "Multiplayer.c"
#include "Definitions.h"

void intialisation();
int  menuMakeChoice(char menu[][MenuWordLen], int size, int pos);
void drawMenu(char menu[][MenuWordLen], int size, int pos);
int  prepareGame(int gType, int *dif, chtype*** field, bool*** mask, struct Figure* f, struct Figure* nextF, long *score, long *timer, struct tm *t1);
int  game(int dif, int gType, bool host);
int  createField(chtype*** field);
int  createMask(bool*** mask);
long getTime(struct tm t1);
int  drawField(chtype** field, int player);
int  addScore(int dif, long *score, chtype** field, bool** mask, struct Figure *f);
int  getNextFigure(int dif, struct Figure** f, struct Figure ** nextF);
int  drawSecondWindow(int player); /*empty window for next figure, scores, and time*/
void drawBorder(int x1, int y1, int x2, int y2, int style);
int  showScore(long score, int player);
int  showTime(unsigned long int time, int player);
int  saveGame(int dif, chtype** field, bool** mask, struct Figure* f, struct Figure* nextF, long score, long timer);
int  writeLong(FILE *f, long l); /*write to file one (long) element with xor encrypting*/
int  loadGame(int *dif, chtype** field, bool** mask, struct Figure* f, struct Figure* nextF, long *score, long *timer);
void readLong(FILE *f, long *l); /*read from file one (long) element with xor encrypting*/
int  freeGame(chtype*** field, bool*** mask, struct Figure **f, struct Figure **nextF);
int  showTopScores();
int  addTopScores(long score);
void showScrollScreen(char *fName);
void timeToString(unsigned long int score, char str[]);
int  createFigure(struct Figure *f, int dif);
int  showFigure(struct Figure* f, int player);
int  showNextFigure(struct Figure f, int player);
int  rotateFigureLeft(struct Figure* f);
int  rotateFigureRight(struct Figure* f);
int  canMove(bool** mask, struct Figure* f, int dx, int dy); /*checking if figure can move in dx, dy position*/
int  insertFigure(chtype** field, bool** mask, struct Figure* f);
int  checkLines(chtype** field, bool** mask); /*checking and deleting full lines*/
int  deleteLine(chtype** field, bool** mask, int num);
void putChtype(int x, int y, chtype ch); /*draw one element in x, y*/
void intToString(unsigned long int i, char s[]);
int  showFullScreen(char* s);

int main(int argc,char *argv[])
{
	intialisation();
	
	bool cont = TRUE, gcont = TRUE;
	int gType;
	int mpos = 0, msize = 7;
	long gres = 0;
	char mMenu[msize][MenuWordLen];
	
	strcpy(mMenu[0], "New Game");
	strcpy(mMenu[1], "Load Game");
	strcpy(mMenu[2], "Multi-Player");
	strcpy(mMenu[3], "Top Scores");
	strcpy(mMenu[4], "Game Rules");
	strcpy(mMenu[5], "Credits");
	strcpy(mMenu[6], "Exit");
	
	int difpos = 0, difsize = 4;
	char difMenu[difsize][MenuWordLen];
	
	strcpy(difMenu[0], "EASY");
	strcpy(difMenu[1], "MEDIUM");
	strcpy(difMenu[2], "HARD");
	strcpy(difMenu[3], "Exit");
	
	do{
		mpos = menuMakeChoice(mMenu, msize, mpos); /*Main menu choice*/
		if (mpos == 0 || mpos == 1) {
			if (mpos == 0) {
				gType = NewGame;
			} else {
				gType = LoadGame; /* load game*/
			}
			gcont = TRUE;
			do {
				if (gType == LoadGame) {
					gres = game(0, gType, FALSE);
				} else {
					difpos = menuMakeChoice(difMenu, difsize, 0); /*difficult menu choice*/
					if (difpos != 3) {
						gres = game(difpos, gType, FALSE);
					} else {
						gcont = FALSE;
					}
				}
				
				/*choose something based on game result*/
				if (gres == NewGame) { /*New game*/
					gType = NewGame;
				}
				else if (gres == LoadGame) { /*load game*/
					gType = LoadGame;
				} else {
					gcont = FALSE; /*exit if there was an error or something else */
				}
			} while(gcont);
		}
		else if (mpos == 2) { /*multiplayer*/
			int netpos = 0, netsize = 5;
			char netMenu[difsize][MenuWordLen];
			
			strcpy(netMenu[0], "Host EASY");
			strcpy(netMenu[1], "Host MEDIUM");
			strcpy(netMenu[2], "Host HARD");
			strcpy(netMenu[3], "Join");
			strcpy(netMenu[4], "Exit");
			
			netpos = menuMakeChoice(netMenu, netsize, netpos);
			if (netpos == 0) {
				netgame(0,TRUE);
			}
			else if (netpos == 1) {
				game(1, NetGame, TRUE);
			}
			else if (netpos == 2) {
				game(2, NetGame, FALSE);
			}
			else if (netpos == 3) {
				netgame(0, FALSE);
			}
		}
		else if (mpos == 3) { /*Top scores*/
			showTopScores();
		}
		else if (mpos == 4) { /*Game rules*/
			showScrollScreen(RulesFileName);
		}
		else if (mpos == 5) { /*credits*/
			showFullScreen(CreditsString);
			while (getch()==ERR);
		}
		else if (mpos == 6) { /*exit*/
			cont = FALSE;
		}
	}while(cont);
	endwin();
}

void intialisation() {
	initscr();
	keypad(stdscr, TRUE);

	
	if (!has_colors())
	{
		endwin();
		printf("Colors are not supported");
		getch();
		exit(1);
	}
	start_color();
	noecho();
	
	srand (time(0));
	
	/*color initialisation*/
	init_pair(MenuColor, COLOR_RED, COLOR_WHITE);
	init_pair(MenuChColor, COLOR_GREEN, COLOR_BLACK);
	init_pair(FieldScreenColor, COLOR_RED, COLOR_BLACK);
	init_pair(FieldColor,  COLOR_GREEN, COLOR_WHITE);
	init_pair(FigureColor1, COLOR_GREEN, COLOR_RED);
	init_pair(FigureColor2, COLOR_GREEN, COLOR_GREEN);
	init_pair(FigureColor3, COLOR_GREEN, COLOR_MAGENTA);
	init_pair(FigureColor4, COLOR_GREEN, COLOR_YELLOW);
	init_pair(FigureColor5, COLOR_GREEN, COLOR_BLUE);
	init_pair(SecondWindowColor, COLOR_GREEN, COLOR_WHITE);
	init_pair(ScoreColor, COLOR_RED, COLOR_WHITE);
	init_pair(TimeColor, COLOR_BLUE, COLOR_WHITE);
}

int menuMakeChoice(char menu[][MenuWordLen], int size, int pos){
	
	int choice;
	bkgdset((chtype)COLOR_PAIR(MenuColor));
	clear();
	
	drawMenu(menu, size, pos);
	do{
		choice = getch();
		if (choice == KEY_UP) {
			pos = (pos + size - 1 ) % size;
			drawMenu(menu, size, pos);
		}
		else if (choice == KEY_DOWN) {
			pos = (pos + 1) % size;
			drawMenu(menu, size, pos);
		}
		else if (choice == 10) {
			return pos;
		}
	} while(1);
}

void drawMenu(char menu[][MenuWordLen], int size, int pos) {
	int i;
	for (i=0; i<size; ++i) {
		if (pos == i) {
			bkgdset((chtype)COLOR_PAIR(MenuChColor));
		} else {
			bkgdset((chtype)COLOR_PAIR(MenuColor));
		}
		move(5 + i , 15);
		printw("%s",menu[i]);
	}
	refresh();
	move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
}

int createField(chtype*** pfield) {
	int i, j;
	chtype** field;
	field = malloc(FieldHeight*sizeof(chtype*));
	for(i=0; i<FieldHeight; ++i) {
		field[i] = malloc(FieldLength*sizeof(chtype));
		for(j=0; j<FieldLength-1; ++j) {
			field[i][j] = ' ' | COLOR_PAIR(FieldColor);
		}
		field[i][FieldLength-1] = 0;
	}
	*pfield = field;
}

int createMask(bool*** pmask) {
	int i, j;
	bool** mask;
	mask = malloc(FieldHeight*sizeof(bool*));
	for(i=0; i<FieldHeight; ++i){
		mask[i] = malloc((FieldLength-1)*sizeof(bool));
		for(j=0; j<FieldLength-1; ++j){
			mask[i][j] = FALSE;
		}
	}
	*pmask = mask; 
}

int prepareGame(int gType, int *dif, chtype*** field, bool*** mask, struct Figure* f, struct Figure* nextF, long *score, long *timer, struct tm *t1) {
	int lRes;
	time_t rawtime;
	struct tm *t2;
	
	createField(field);
	createMask(mask);
	
	createFigure(f, *dif); /*creating figures*/
	createFigure(nextF, *dif);
	
	time (&rawtime); /*getting time*/
	t2 = localtime (&rawtime);
	t1->tm_sec = t2->tm_sec;
	t1->tm_min = t2->tm_min;
	t1->tm_hour = t2->tm_hour;
	
	if (gType == LoadGame) { /*load game*/
		lRes = loadGame(dif, *field, *mask, f, nextF, score, timer);
		if (lRes == LoadFail) {
			showFullScreen(LoadFailString);
			while (getch()==ERR);
			
			freeGame(field, mask, &f, &nextF);
			
			return LoadFail;
		} else if (lRes == SumFail) {
			showFullScreen(SumFailString);
			while (getch()==ERR);
			
			freeGame(field, mask, &f, &nextF);
			
			return SumFail;
		}
	}
	return 1;
}

int game(int dif, int gType, bool host) {
	chtype** field; /*Player's field*/
	bool** mask; /*Player's field mask*/
	struct Figure *f = malloc(sizeof(struct Figure)), *nextF = malloc(sizeof(struct Figure)); /*Player's figure and next figure*/
	int i = 0, j, choice, netChoice = NetField, p2netChoice = NetField;
	long score = 0, timer = 0, loadTime = 0, p2score; 
	struct tm t1; /*time*/
	int plusScore; /*scores for full lines */
	int s, s2; /* sockets */
	
	chtype** p2field; /*Opponent field*/
	struct Figure *p2f = malloc(sizeof(struct Figure)), *p2nextF = malloc(sizeof(struct Figure)); /*Opponent figure and next figure*/
	
	/*preparing game*/
	if (gType == LoadGame || gType == NewGame) { /*new or load*/
		if (prepareGame(gType, &dif, &field, &mask, f, nextF, &score, &loadTime, &t1) < 0 ) return ExitGame;
	}
	else if (gType == NetGame) { /*multiplayer*/
		if (prepareNetGame(&s, &s2, &dif, host) == NetFail) return NetFail;
		
		sleep(2);
		
		if (prepareGame(FALSE, &dif, &field, &mask, f, nextF, &score, NULL, &t1) < 0 ) return ExitGame;
		createField(&p2field);
		createFigure(p2f, dif);
		createFigure(p2nextF, dif);
	}
	
	timer = loadTime;
	halfdelay(1);
	do{
		/* send and receive all data*/
		if (gType == NetGame) {
			int a = sendRecvAll(s, s2, &netChoice, &p2netChoice, field, f, nextF, score, 
									p2field, p2f, p2nextF, &p2score);
			if ( a == NetFail) {
				while (getch()!=' ');
				return NetFail;	
			}
			else if (a == NetEnd) {
				break;
			}
		}
		
		/*drawing game screen(s)*/
		bkgdset((chtype)COLOR_PAIR(FieldScreenColor));	
		clear();
		drawField(field, 0);
		showFigure(f, 0);
		drawSecondWindow(0);
		showNextFigure(*nextF, 0);
		showScore(score, 0);
		showTime(timer, 0);
		if (gType == NetGame) {
			drawField(p2field, 1);
			showFigure(p2f, 1);
			drawSecondWindow(1);
			showNextFigure(*p2nextF, 1);
			showScore(p2score, 1);
			showTime(timer, 1);
		}
		refresh();
		
		/*getting player's entered key */
		choice = getch();
		if (choice == KEY_UP) {
			rotateFigureRight(f);
			if (!canMove(mask, f, 0, 0)) {
				rotateFigureLeft(f);
			}
		}
		else if (choice == KEY_DOWN) {
			i = 10 - 4*dif;
		}
		else if (choice == KEY_LEFT) {
			if (canMove(mask, f, -1, 0)) {
				--(f->x);
			}
		}
		else if (choice == KEY_RIGHT) {
			if (canMove(mask, f, 1, 0)) {
				++(f->x);
			}
		}
		else if (choice == 27 && gType != NetGame) {
			int gpos = 0, gsize = 5;
			char gMenu[gsize][MenuWordLen]; /* game menu*/
			
			strcpy(gMenu[0], "Resume");
			strcpy(gMenu[1], "New Game");
			strcpy(gMenu[2], "Load Game");
			strcpy(gMenu[3], "Save Game");
			strcpy(gMenu[4], "Exit");
			
			gpos = menuMakeChoice(gMenu, gsize, 0);
			
			if (gpos == 1) {
				freeGame(&field, &mask, &f, &nextF);			
				return NewGame;
			}
			else if (gpos == 2) {
				freeGame(&field, &mask, &f, &nextF);
				return LoadGame;
			}
			else if (gpos == 3) {
				saveGame(dif, field, mask, f, nextF, score, timer);
			}
			else if (gpos == 4) {
				freeGame(&field, &mask, &f, &nextF);
				return ExitGame;
			}
			/*drawing game screen if player resumed to game*/
			bkgdset((chtype)COLOR_PAIR(FieldScreenColor));
			clear();
			drawField(field, 0);
			showFigure(f, 0);
			drawSecondWindow(0);
			showNextFigure(*nextF, 0);
			showScore(score, 0);
			showTime(timer, 0);		
			refresh();
		}
		
		/*for smooth moving*/
		chtype c;
		if (choice != ERR) c = getch();
		if (gType == NetGame) {
			if (c != ERR) c = getch();
		}
		
		/*time changes*/
		timer = loadTime + getTime(t1);
      	
		++i;
		if (i >= 10 - 4*dif) { /*figure fall's*/
			if (canMove(mask, f, 0, 1)) { /*figure can move down*/
				++(f->y);
				if (gType == NetGame) netChoice = NetFigure;
			} else { /*figure can not move down*/
				if (gType == NetGame) netChoice = NetField;
				addScore(dif, &score, field, mask, f);
				
				/*getting new figure*/
				getNextFigure(dif, &f, &nextF);
				
				if (!canMove(mask, f, 0, 0)) { /*GAME OVER*/
					if (gType == NetGame) {
						netChoice = NetLost;
					} else {
						if (gType == NewGame) {
							addTopScores(score); /* writing to top scores*/
						} else showTopScores();
						freeGame(&field, &mask, &f, &nextF);
						return LostGame;
					}
				}
			}
			i = 0;
		}
		if (choice == 27 && gType == NetGame) {
			netChoice = NetExit;
		}
	} while(1);
	
	/*deleting player's and opponent's fied's, mask and figures. */
	freeGame(&field, &mask, &f, &nextF);
	freeGame(&p2field, NULL, &p2f, &p2nextF);
	
	/*ending game*/
	if (netChoice == NetLost) {
		showFullScreen("Boooo!!! YOU Lost!!!!!");
		move(15, 10);
		printw("Press ESC to exit.");
		move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
		while (getch()!=27);
	}
	else if (p2netChoice == NetLost) {
		showFullScreen("Yeah!!! YOU Win!!!!!");
		move(15, 10);
		printw("Press ESC to exit.");
		move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
		while (getch()!=27);
	}
	else if (p2netChoice == NetExit) {
		showFullScreen("Sorry, Player 2 quit...");
		move(15, 10);
		printw("Press ESC to exit.");
		move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
		while (getch()!=27);
	}
	
	/*closing sockets*/
	close(s2);
	if (host) {
		close(s);
	}
}

int drawField(chtype** field, int player){
	int i, j;
	drawBorder(player*(FieldLength*2 + 16), 0, player*(FieldLength*2 + 16) + FieldLength*2 - 1, FieldHeight+1, FieldScreenColor);
	
	for(i=0; i<FieldHeight; ++i) {
		for(j=0; j<FieldLength-1; ++j) {
			putChtype(player*(FieldLength + 8)+1+j,1+i,field[i][j]);
		}
	}
	if (!player) {
		bkgdset((chtype)COLOR_PAIR(FieldScreenColor));
		move(21,7);
		printw("Press ESC to exit to game menu.");
		move(22,7);
		printw("Key Left and Key Right to move figure.");
		move(23,7);
		printw("Key Up to rotate figure.");
		move(24,7);
		printw("Kye Down to speed up falling.");
	}
}

long getTime(struct tm t1) {
	time_t rawtime;
	struct tm *t2;
	time (&rawtime);
	t2 = localtime (&rawtime);
	return t2->tm_hour*3600 + t2->tm_min*60 + t2->tm_sec - t1.tm_hour*3600 - t1.tm_min*60 - t1.tm_sec;
}

int addScore(int dif, long *score, chtype** field, bool** mask, struct Figure *f) {
	++(*score);
	int plusScore;
	insertFigure(field, mask, f);
	plusScore = checkLines(field, mask);
	if (plusScore) { /*adding scores*/
		*score += (5+2*dif)*plusScore+dif*(plusScore-1);
	}
	printf("\a");
}

int getNextFigure(int dif, struct Figure** f, struct Figure ** nextF) {
	free((*f)->p);
	free(*f);
	*f = *nextF;
	
	*nextF = malloc(sizeof(struct Figure));
	createFigure(*nextF, dif);
}

int  drawSecondWindow(int player) {
	int i, j;
	drawBorder(FieldLength*2 + 1 + player*(FieldLength*2 + 16), 0, FieldLength*2 + 13 + player*(FieldLength*2 + 16), FieldHeight+1, SecondWindowColor);
		
	for(i=1; i<FieldHeight+1; ++i) {
		for(j=FieldLength*2 + 2; j<FieldLength*2 + 13; ++j) {
			move(i, j + player*(FieldLength*2 + 16));
			addch(' ' | COLOR_PAIR(SecondWindowColor));
		}
	}
	
	char s[30];
	strcpy(s,"Player ");
	s[7] = '1' + player;
	s[8] = 0; 
	bkgdset((chtype)COLOR_PAIR(SecondWindowColor));
	move(1, 1+FieldLength*2 + 1 + player*(FieldLength*2 + 16));
	printw("%s", s);
}

void drawBorder(int x1, int y1, int x2, int y2, int style){
	move(y1,x1);
	addch(ACS_ULCORNER | COLOR_PAIR(style));
	move(y2,x2);
	addch(ACS_LRCORNER | COLOR_PAIR(style));
	move(y1,x2);
	addch(ACS_URCORNER | COLOR_PAIR(style));
	move(y2,x1);
	addch(ACS_LLCORNER | COLOR_PAIR(style));
	int i;
	for(i=x1+1; i<x2; ++i){
		move(y1,i);
		addch(ACS_HLINE | COLOR_PAIR(style));
		move(y2,i);
		addch(ACS_HLINE | COLOR_PAIR(style));
	}
	for(i=y1+1; i<y2; ++i){
		move(i, x1);
		addch(ACS_VLINE | COLOR_PAIR(style));
		move(i, x2);
		addch(ACS_VLINE | COLOR_PAIR(style));
	}
}

int showScore(long score, int player) {
	int i;
	char s[10] = "", n[10] = "";
	
	bkgdset((chtype)COLOR_PAIR(ScoreColor));
	move(10, FieldLength*2+4 + player*(FieldLength*2 + 16));
	printw(" Score:");
	
	move(11, FieldLength*2+4 + player*(FieldLength*2 + 16));
	intToString(score, s);
	for(i=0; i<7-strlen(s); ++i) {
		strcat(n,"0");
	}
	strcat(n, s);
	printw("%s", n);
}

int showTime(unsigned long int time, int player) {
	char s[30];
	move(15, FieldLength*2+2 + player*(FieldLength*2 + 16));
	bkgdset((chtype)COLOR_PAIR(TimeColor));
	printw("   Time : ");
	
	timeToString(time, s);
	move(16, FieldLength*2+2 + player*(FieldLength*2 + 16));
	printw("%s", s);
	move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);	
}

int saveGame(int dif, chtype** field, bool** mask, struct Figure* f, struct Figure* nextF, long score, long timer) {
	FILE *fout;
	long temp, sum = 0;
	
	fout = fopen(SaveFileName, "w");
	if (f != NULL) {
		/*saving difficulty*/
		temp = (long) dif;
		sum = temp;
		writeLong(fout, temp);
		/*saving score*/
		sum += score;
		writeLong(fout, score);
		/*saving time*/
		sum +=timer;
		writeLong(fout, timer);
		/*saving field*/
		int i, j;
		for(i=0; i<FieldHeight; ++i){
			for(j=0; j<FieldLength-1; ++j){
				temp = (long) field[i][j];
				sum += temp;
				writeLong(fout, temp);
			}
		}
		/*saving mask*/
		for(i=0; i<FieldHeight; ++i){
			for(j=0; j<FieldLength-1; ++j){
				temp = (long) mask[i][j];
				sum += temp;
				writeLong(fout, temp);
			}
		}
		
		/*saving figure*/
		temp = (long) f->x;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) f->y;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) f->style;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) f->size;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) f->num;
		sum += temp;
		writeLong(fout, temp);
		
		for(i=0; i<f->size; ++i){
			temp = (long) f->p[i].x;
			sum += temp;
			writeLong(fout, temp);
			
			temp = (long) f->p[i].y;
			sum += temp;
			writeLong(fout, temp);
		}
		
		/*saving next figure*/
		temp = (long) nextF->x;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) nextF->y;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) nextF->style;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) nextF->size;
		sum += temp;
		writeLong(fout, temp);
		
		temp = (long) nextF->num;
		sum += temp;
		writeLong(fout, temp);
		
		for(i=0; i<nextF->size; ++i){
			temp = (long) nextF->p[i].x;
			sum += temp;
			writeLong(fout, temp);
			
			temp = (long) nextF->p[i].y;
			sum += temp;
			writeLong(fout, temp);
		}
		/*saving checksum*/
		writeLong(fout, sum);
		
		fclose(fout);
	} else {
		return SaveFail;
	}
}

int writeLong(FILE *f, long l) {
	l = l ^ 1431655765L; /*1010...10101 - 32 digits*/
	fwrite(&l, sizeof(l), 1, f);
}

int loadGame(int *dif, chtype** field, bool** mask, struct Figure *f, struct Figure *nextF, long *score, long *timer) {
	FILE *fin;
	long temp, sum = 0;
	
	fin = fopen(SaveFileName, "r");
	if (fin != NULL) {
		/*loading difficulty*/
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		*dif = (int) temp;
		sum = temp;
		
		/*loading score*/
		if (feof(fin)) return LoadFail;
		readLong(fin, score);
		sum += *score;
		
		/*loading time*/
		if (feof(fin)) return LoadFail;
		readLong(fin, timer);
		sum += *timer;
		
		/*loading field*/
		int i, j;
		for(i=0; i<FieldHeight; ++i){
			for(j=0; j<FieldLength-1; ++j){
				if (feof(fin)) return LoadFail;
				readLong(fin, &temp);
				field[i][j] = (chtype) temp;
				sum += temp;
			}
		}
		
		/*loading mask*/
		for(i=0; i<FieldHeight; ++i){
			for(j=0; j<FieldLength-1; ++j){
				if (feof(fin)) return LoadFail;
				readLong(fin, &temp);
				mask[i][j] = (bool) temp;
				sum += temp;
			}
		}
		
		/*loading figure*/
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		f->x = (int) temp;
		sum += temp;
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		f->y = (int) temp;
		sum += temp;
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		f->style = (int) temp;
		sum += temp;

		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		f->size = (int) temp;
		sum += temp;
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		f->num = (int) temp;
		sum += temp;
		
		free(f->p);
		f->p = malloc(f->size*sizeof(struct Point));
		
		for(i=0; i<f->size; ++i){
			if (feof(fin)) return LoadFail;
			readLong(fin, &temp);
			f->p[i].x = (int) temp;
			sum += temp;

			if (feof(fin)) return LoadFail;
			readLong(fin, &temp);
			f->p[i].y = (int) temp;
			sum += temp;			
		}
		
		/*loading next figure*/
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		nextF->x = (int) temp;
		sum += temp;
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		nextF->y = (int) temp;
		sum += temp;

		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		nextF->style = (int) temp;
		sum += temp;
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		nextF->size = (int) temp;
		sum += temp;
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &temp);
		nextF->num = (int) temp;
		sum += temp;
		
		free(nextF->p);
		nextF->p = malloc(f->size*sizeof(struct Point));
		
		for(i=0; i<nextF->size; ++i){
			if (feof(fin)) return LoadFail;
			readLong(fin, &temp);
			nextF->p[i].x = (int) temp;
			sum += temp;

			if (feof(fin)) return LoadFail;
			readLong(fin, &temp);
			nextF->p[i].y = (int) temp;
			sum += temp;			
		}
		
		long checkSum;
		
		/*loading check sum*/
		if (feof(fin)) return LoadFail;
		readLong(fin, &checkSum);		
		
		/*sum checking*/
		if (sum != checkSum) {
			return SumFail;
		}
		
		fclose(fin);
		return 1;
	} else {
		return LoadFail;
	}
}

void readLong(FILE *f, long *l) {
	fread(l, sizeof(long), 1, f);
	*l = *l ^ 1431655765L; /*1010...10101 - 32 digits*/
}

int freeGame(chtype*** field, bool*** mask, struct Figure **f, struct Figure **nextF) {
	int i;
	for(i=0; i<FieldHeight; ++i) {
		free((*field)[i]);
	}
	if (mask != NULL) {
		for(i=0; i<FieldHeight; ++i) {
			free((*mask)[i]);
		}
	}
	
	free(*field);
	if (mask != NULL) free(*mask);
	free((*f)->p);
	free((*nextF)->p);
	free((*f));
	free((*nextF));
}

int showTopScores() {
	FILE *fin;
	long temp, sum = 0, checkSum;
	int i, j;
	long topScores[TopScoreNum];
	char topNames[TopScoreNum][TopNameLength], s[TopNameLength];
	
	/*fill score with 0 and names with <empty>*/
	for(i=0; i<TopScoreNum; ++i) {
		topScores[i] = 0;
		strcpy(topNames[i], "<Empty>");
	}
	
	/*loading names and scores from file*/
	fin = fopen(TopFileName, "r");
	if (fin != NULL) {
		for(i=0; i<TopScoreNum; ++i) {
			if (feof(fin)) return LoadFail;
			readLong(fin, &temp);
			topScores[i] = temp;
			sum += temp;
			for(j=0; j<TopNameLength; ++j) {
				if (feof(fin)) return LoadFail;
				readLong(fin, &temp);
				topNames[i][j] = (char) temp;
				sum += temp;
			}
		}
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &checkSum);		
		
		if (sum != checkSum) { /*if checksum didn't match fill score with 0 and names with <empty>*/
			for(i=0; i<TopScoreNum; ++i) {
				topScores[i] = 0;
				strcpy(topNames[i], "<Empty>");
			}
		}
		
		fclose(fin);
	}
	
	/*show top scores */
	bkgdset((chtype)COLOR_PAIR(MenuColor));
	clear();
	
	move(3, 10);
	printw(" TOP SCORES : ");
	
	for(i=0; i<TopScoreNum; ++i) {
		move(5+i, 5);
		printw("%d", i+1);
		
		move(5+i, 8);
		printw("%s", topNames[i]);
		
		move(5+i, 29);
		printw("%d", topScores[i]);
	}
	move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
	while (getch()==ERR);
	return 1;
}

int addTopScores(long score){
	FILE *fin, *fout;
	long temp, sum = 0;
	int i, j;
	long checkSum;
	long topScores[TopScoreNum+1];
	char topNames[TopScoreNum+1][TopNameLength], tempS[TopNameLength];
	
	for(i=0; i<TopScoreNum+1; ++i) { /*fill Scores with 0 and Names with <empty>*/
		topScores[i] = 0;
		strcpy(topNames[i], "<Empty>");
	}
	
	fin = fopen(TopFileName, "r");
	if (fin != NULL) {
		for(i=0; i<TopScoreNum; ++i) { /* fill Scores and Names with info from file*/
			if (feof(fin)) return LoadFail;
			readLong(fin, &temp);
			topScores[i] = temp;
			sum += temp;
			for(j=0; j<TopNameLength; ++j) {
				if (feof(fin)) return LoadFail;
				readLong(fin, &temp);
				topNames[i][j] = (char) temp;
				sum += temp;
			}
		}
		
		if (feof(fin)) return LoadFail;
		readLong(fin, &checkSum);		
		
		if (sum != checkSum) { /*if checksum didn't match fill Scores with 0 and Names with <empty>*/ 
			for(i=0; i<TopScoreNum+1; ++i) {
				topScores[i] = 0;
				strcpy(topNames[i], "<Empty>");
			}
		}
		fclose(fin);
		sum = 0;
	}
	
	topScores[TopScoreNum] = score;
	temp = TopScoreNum;
	for(i=TopScoreNum; i > 0; --i) { /*sorting*/
		if (topScores[i] > topScores[i-1]) {
			temp = topScores[i];
			topScores[i] = topScores[i-1];
			topScores[i-1] = temp;
			
			temp = i - 1;
			strcpy(tempS, topNames[i-1]);
			strcpy(topNames[i-1], topNames[i]);
			strcpy(topNames[i], tempS);
		}
	}
	
	/*getting players name*/
	if (temp != TopScoreNum) {
		bkgdset((chtype)COLOR_PAIR(MenuColor));
		clear();
			
		move(3, 10);
		printw(" Please, enter your name : ");
		
		move(4, 15);
		echo();
		scanw("%s", tempS);
		tempS[TopNameLength - 1] = 0;
		noecho();
		strcpy(topNames[temp], tempS);
	}
	/*writting results to file */
	fout = fopen(TopFileName, "w");
	if (fout != NULL) {
		for(i=0; i<TopScoreNum; ++i) { 
			temp = topScores[i];
			sum += temp;
			writeLong(fout, temp);
			for(j=0; j<TopNameLength; ++j) {
				temp = (long) topNames[i][j];
				sum += temp;
				writeLong(fout, temp);
			}
		}
		writeLong(fout, sum);
		fclose(fout);
	}
	showTopScores();
	return 1;
}

void showScrollScreen(char *fName) {
	
	FILE* fin;
	long pos = 0;
	bool cont = TRUE, down = TRUE;
	int i, j, downCount = 0, upCount = 0, temp = 0;
	char ch;
	
	do {
		bkgdset((chtype)COLOR_PAIR(MenuColor));
		clear();
		fin = fopen(fName, "r");
		/*finding right position */
		if (fin != NULL) {
			if (pos >= ScrollX + 10) {
				i = pos - ScrollX - 5;
			} else {
				i = 0;
			}
			fseek(fin, i, SEEK_SET);
			for (; i<pos; ++i) {
				if (!feof(fin)) {
					fread(&ch, sizeof(ch), 1, fin);
					++temp;
					if (ch == '\n') {
						upCount = temp % ScrollX; /*position changes when pressed key_up*/
						temp = 0;
					}
				} else {
					down = FALSE;
				}
			}
			if (ch!='\n') upCount = ScrollX; /*position changes when pressed key_up*/
			for (i=0; i<ScrollY; ++i) {
				for (j=0; j<ScrollX; ++j) {
					if (!feof(fin)) {
						fread(&ch, sizeof(ch), 1, fin);
						if (ch=='\n') {
							if (!i) downCount++; /*position changes when pressed key_down*/
							j = ScrollX;
						} else {
							if (!i) downCount++; /*position changes when pressed key_down*/
							move(5 + i, 5 + j);
							printw("%c", ch); /*drawing text*/
						}
					} else {
						down = FALSE;
					}
				}
			}
			
			/*drawing other*/
			drawBorder(4, 4, ScrollX + 5, ScrollY + 5, MenuColor);
			move(6,ScrollX + 7);
			addch(ACS_UARROW);
			move(ScrollY + 3,ScrollX + 7);
			addch(ACS_DARROW);
			move(ScrollY + 7,7);
			printw("Press ESC to exit");
			
			move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
			fclose(fin);
		} else {
			cont = FALSE;
		}
		
		do { 
			i = getch();
		} while (i!=KEY_UP && i!=KEY_DOWN && i!=27);
		
		if (i == KEY_UP) {
			pos -= upCount;
			upCount = 0;
			downCount = 0;
			down = TRUE;
			if (pos < 0) pos = 0;
		} 
		else if (i == KEY_DOWN) {
			if (down) {
				pos += downCount;
				upCount = 0;
				downCount = 0;
			}
		}
		else if (i == 27) {
			cont = FALSE;
		}
	} while(cont);
}

void timeToString(unsigned long int score, char str[]) {
	char word[20], s[10];
	int sec, i;
	sec   = score % 60;
  	score = score / 60;

  	strcpy(s, "");
  	strcpy(word, "");
  	strcpy(str, "");

  	intToString(score, word);
  	for(i=strlen(word);i<3;++i) strcat(s," ");
  	strcat(str, s);
  	strcat(str, word);
  	strcat(str, " m ");
  	intToString(sec, word);

 	if (sec<10)
  	{
	    word[1] = word[0];
    	word[0] = '0';
    	word[2] = 0;
  	}

  	strcat(str, word);
  	strcat(str, " s");
}

int createFigure(struct Figure *f, int dif){
	int choice;
	
	f->x = FieldLength / 2;
	f->y = 1;
	static int style = 0;
	if (style==0) {
		f->style = FigureColor1;
	}
	else if (style==1) {
		f->style = FigureColor2;
	}
	else if (style==2) {
		f->style = FigureColor3;
	}
	else if (style==3) {
		f->style = FigureColor4;
	}
	else if (style==4) {
		f->style = FigureColor5;
	}
	style = (style + 1) % 5;
	
	f->num = rand() % 10 + 2*dif;
	if (f->num == 0) { /* .*/
		f->size = 1;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;
	}
	else if (f->num == 1) { /* |*/
		f->size = 2;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;
		f->p[1].x = 0;
		f->p[1].y = 1;
	}
	else if (f->num == 2) { /* >*/
		f->y = 2;	
		f->size = 3;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = 1;
		f->p[1].y = 0;		
		f->p[2].x = 0;
		f->p[2].y = -1;		
	}
	else if (f->num == 3) { /* T*/
		f->y = 2;	
		f->size = 4;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = 1;
		f->p[1].y = 0;		
		f->p[2].x = -1;
		f->p[2].y = 0;		
		f->p[3].x = 0;
		f->p[3].y = -1;		
	}
	else if (f->num == 4) { /* L*/
		f->y = 2;
		f->size = 4;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;
		f->p[1].x = 1;
		f->p[1].y = 1;
		f->p[2].x = 0;
		f->p[2].y = 1;
		f->p[3].x = 0;
		f->p[3].y = -1;
	}
	else if (f->num == 5) { /* J*/
		f->y = 2;
		f->size = 4;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;
		f->p[1].x = -1;
		f->p[1].y = 1;
		f->p[2].x = 0;
		f->p[2].y = 1;
		f->p[3].x = 0;
		f->p[3].y = -1;
	}
	else if (f->num == 6) { /* S*/
		f->size = 4;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = 1;
		f->p[1].y = 0;		
		f->p[2].x = 0;
		f->p[2].y = 1;		
		f->p[3].x = -1;
		f->p[3].y = 1;		
	}
	else if (f->num == 7) { /* Z*/
		f->size = 4;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = -1;
		f->p[1].y = 0;		
		f->p[2].x = 0;
		f->p[2].y = 1;		
		f->p[3].x = 1;
		f->p[3].y = 1;		
	}
	else if (f->num == 8) { /* I*/
		f->y = 2;
		f->size = 4;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = 0;
		f->p[1].y = -1;		
		f->p[2].x = 0;
		f->p[2].y = 1;		
		f->p[3].x = 0;
		f->p[3].y = 2;		
	}
	else if (f->num == 9) { /* O*/
		f->size = 4;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = -1;
		f->p[1].y = 0;		
		f->p[2].x = 0;
		f->p[2].y = 1;		
		f->p[3].x = -1;
		f->p[3].y = 1;		
	}
	else if (f->num == 10) { /* T*/
		f->size = 5;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = -1;
		f->p[1].y = 0;		
		f->p[2].x = 1;
		f->p[2].y = 0;		
		f->p[3].x = 0;
		f->p[3].y = 1;
		f->p[4].x = 0;
		f->p[4].y = 2;		
				
	}
	else if (f->num == 11) { /* [*/
		f->size = 5;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = -1;
		f->p[1].y = 0;		
		f->p[2].x = 1;
		f->p[2].y = 0;		
		f->p[3].x = -1;
		f->p[3].y = 1;		
		f->p[4].x = 1;
		f->p[4].y = 1;		
	}
	else if (f->num == 12) { /* e*/
		f->y = 2;
		f->size = 6;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = -1;
		f->p[1].y = 0;		
		f->p[2].x = 1;
		f->p[2].y = -1;		
		f->p[3].x = -1;
		f->p[3].y = -1;		
		f->p[4].x = 0;
		f->p[4].y = -1;		
		f->p[5].x = -1;
		f->p[5].y = 1;		
	}
	else if (f->num == 13) { /* ]-*/
		f->y = 2;
		f->size = 6;
	
		f->p = malloc(f->size*sizeof(struct Point));
	
		f->p[0].x = 0;
		f->p[0].y = 0;		
		f->p[1].x = -1;
		f->p[1].y = 0;		
		f->p[2].x = 1;
		f->p[2].y = 0;		
		f->p[3].x = -1;
		f->p[3].y = 1;		
		f->p[4].x = 1;
		f->p[4].y = 1;
		f->p[5].x = 0;
		f->p[5].y = -1;		
	}
}

int  showFigure(struct Figure* f, int player){
	int i;
	chtype ch;
	for(i=0; i<f->size; ++i){
		ch = FigureElem | COLOR_PAIR(f->style);
		putChtype(f->x + f->p[i].x + player*(FieldLength + 8), f->y + f->p[i].y, ch);
	}
}

int  showNextFigure(struct Figure f, int player) {
	f.x = FieldLength + 4;
	f.y = 5;
	showFigure(&f, player);
	
}

int  rotateFigureLeft(struct Figure* f){
	int i, temp;
	for(i=0; i<f->size; ++i){
		temp = f->p[i].x;
		f->p[i].x = f->p[i].y;
		f->p[i].y = -temp;
	}
}

int  rotateFigureRight(struct Figure* f){
	int i, temp;
	for(i=0; i<f->size; ++i){
		temp = f->p[i].x;
		f->p[i].x = -f->p[i].y;
		f->p[i].y = temp;
	}
}

int canMove(bool** mask, struct Figure* f, int dx, int dy){
	int i;
	for(i=0; i<f->size; ++i) {		
		if ((f->x + f->p[i].x + dx >= FieldLength) || (f->x + f->p[i].x + dx < 1)
				|| (f->y + f->p[i].y + dy > FieldHeight) || (f->y + f->p[i].y + dy < 1)) {
			return 0;
		}
		if (mask[f->y + f->p[i].y + dy - 1][f->x + f->p[i].x + dx - 1] == TRUE) {
			return 0;
		}
	}
	return 1;
}

int insertFigure(chtype** field, bool** mask, struct Figure* f){
	int i, j;
	for(i=0; i<(f->size); ++i) {
		mask  [f->y + f->p[i].y - 1][f->x + f->p[i].x - 1] = TRUE;
		field[f->y + f->p[i].y - 1][f->x + f->p[i].x - 1] = FigureElem | COLOR_PAIR(f->style);
	}
}

int checkLines(chtype** field, bool** mask){
	int i, j;
	bool del;
	int result = 0;
	for(i=0; i< FieldHeight; ++i) {
		del = TRUE;
		for(j=0; j<FieldLength-1; ++j) {
			if(!mask[i][j]) {
				del = FALSE;
				break;
			}
		}
		if (del) {
			deleteLine(field, mask, i);
			result++;
		}
	}
	
	if (result) {
		printf("\a");
	}
	return result;
}

int deleteLine(chtype** field, bool** mask, int num){
	int i, j;
	for(i=num; i>0; --i) {
		for(j=0; j<FieldLength-1; ++j) {
			mask  [i][j] = mask  [i-1][j];
			field[i][j] = field[i-1][j];
		}
	}
	for(j=0; j<FieldLength-1; ++j) {
		mask  [0][j] = FALSE;
		field[0][j] = ' ' | COLOR_PAIR(FieldColor);
	}
}

void putChtype(int x, int y, chtype ch){
	move(y,x*2);
	addch(ch);
	move(y,x*2-1);
	addch(ch);
	move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
}

void intToString(unsigned long int i, char s[]) {
	int j=0;
	char c;
	do {
		s[j] = '0' + (i % 10);
		i = i / 10;
		j++;
	}while(i>0);
	s[j] = 0;
	for(i=0;i<j/2;++i)
	{
		c = s[i];
		s[i] = s[j-i-1];
		s[j-i-1] = c;
	}
}

int showFullScreen(char* s) {
	bkgdset((chtype)COLOR_PAIR(MenuColor));
	clear();
	move(5,5);
	printw(s);
	move(getmaxy(stdscr)-1, getmaxx(stdscr)-1);
}
